/*

Miranda IM: the free IM client for Microsoft* Windows*

Copyright 2000-2007 Miranda ICQ/IM project, 
all portions of this codebase are copyrighted to the people 
listed in contributors.txt.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or ( at your option ) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
aLONG with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

/**
 * System Includes:
 **/
#include "commonheaders.h"
#include "m_contacts.h"
#include "svcTimezone.h"
#include "ExtraServices.h"

namespace NServices 
{
	namespace ContactInfo
	{
		#define  CI_TCHAR( ci )	( (( ci )->dwFlag & CNF_UNICODE ) ? DBVT_WCHAR : DBVT_ASCIIZ )

		#define NAMEORDERCOUNT 8
		static BYTE	gNameOrder[NAMEORDERCOUNT];	// name order as set up for contact list

		/**
		 * This function translates the DBVARIANT structure to an CONTACTINFO structure
		 * and keeps the original data type.
		 *
		 * @warning	ci MUST NOT be NULL and dbv must be freed by caller on failure!
		 *					
		 * @param dbv	- DBVARIANT to take the data for translation from
		 * @param	ci	- CONTACTINFO structure to translate to
		 *
		 * @retval 0	- success
		 * @retval 1	- failure
		 **/
		static __forceinline INT VarToVarCI( const DBVARIANT *dbv, CONTACTINFO *ci )
		{
			switch( dbv->type ) 
			{
			case DBVT_ASCIIZ:
			case DBVT_WCHAR:
				{
					// string translation is to be done by caller!!!
					ci->pszVal = dbv->ptszVal;
					ci->type = ( ci->pszVal != NULL ) ? CNFT_ASCIIZ : 0;
				}
				break;

			case DBVT_BYTE:
				{
					ci->type = CNFT_BYTE;
					ci->bVal = dbv->bVal;
				}
				break;

			case DBVT_WORD:
				{
					ci->type = CNFT_WORD;
					ci->wVal = dbv->wVal;
				}
				break;

			case DBVT_DWORD:
				{
					ci->type = CNFT_DWORD;
					ci->dVal = dbv->dVal;
				}
				break;

			default:
				{
					ci->type = 0;
				}
			}
			return ci->type == 0;
		}

		/**
		 * This function tries to read a setting from the contact's protocol module.
		 *
		 * @warning	ci MUST NOT be NULL!
		 *
		 * @param	ci				- pointer to a CONTACTINFO structure holding information about the contact
		 * @param	szSetting	- the desired setting to read
		 *
		 * @retval	0 -	if setting was found and read correctly
		 * @retval	1 - if any error occured or setting was not found
		 **/
		static __forceinline INT GCIVar( CONTACTINFO *ci, LPCSTR szSetting )
		{
			DBVARIANT dbv;
			
			if( DB::Setting::Get( ci->hContact, ci->szProto, szSetting, &dbv, CI_TCHAR( ci ) ) == 0 )
			{
				if( VarToVarCI( &dbv, ci ) )
				{
					// On a error, we need to make sure, read data is cleared out!
					DB::Variant::Free( &dbv );
				}
			}
			else
			{
				ci->type = 0;
			}
			return ci->type == 0;
		}

		/**
		 * This function tries to read a setting from a certain module ( e.g. USERINFO ) and if failed it 
		 * tries once again with the baseprotocol of the contact ( e.g. ICQ ). If nothing was found anyway
		 * and this is an metacontact it can have a look into subcontacts to retrieve the information.
		 * This depends on the settings the user did.
		 *
		 * @warning	ci MUST NOT be NULL!
		 *
		 * @param	ci				- pointer to a CONTACTINFO structure holding information about the contact
		 * @param	szSetting	- the desired setting to read
		 *
		 * @retval	0 -	if setting was found and read correctly
		 * @retval	1 - if any error occured or setting was not found
		 **/
		static __forceinline INT GCIVarEx( CONTACTINFO *ci, LPCSTR szSetting )
		{
			DBVARIANT dbv;
			
			if( DB::Setting::GetEx( ci->hContact, USERINFO, ci->szProto, szSetting, &dbv, CI_TCHAR( ci ) ) == 0 )
			{
				if( VarToVarCI( &dbv, ci ) )
				{
					// On a error, we need to make sure, read data is cleared out!
					DB::Variant::Free( &dbv );
				}
			}
			else
			{
				ci->type = 0;
			}
			return ci->type == 0;
		}

		/**
		 * This function read a setting from the baseprotocol of the contact ( e.g. ICQ ). 
		 *
		 * @warning	ci MUST NOT be NULL!
		 *
		 * @param	ci				- pointer to a CONTACTINFO structure holding information about the contact
		 * @param	szSetting	- the desired setting to read
		 *
		 * @retval	0 -	if setting was found and read correctly
		 * @retval	1 - if any error occured or setting was not found
		 **/
		static __forceinline INT GCIStr( CONTACTINFO *ci, LPCSTR szSetting )
		{
			const BYTE type = CI_TCHAR( ci );
			DBVARIANT dbv;
			
			if( DB::Setting::Get( ci->hContact, ci->szProto, szSetting, &dbv, type ) == 0 )
			{
				if( DB::Variant::ToString( &dbv, type ) == 0 )
				{
					ci->pszVal = dbv.ptszVal;
				}
				else
				{
					DB::Variant::Free( &dbv );
					ci->pszVal = NULL;
				}
			}
			else
			{
				ci->pszVal = NULL;
			}
			ci->type = ( ci->pszVal ) ? CNFT_ASCIIZ : 0;
			return ci->type == 0;
		}

		/**
		 * Format the full name for the contact.
		 *
		 * @params	ci	- CONTACTINFO structure
		 *
		 * @retval	0 -	if setting was found and read correctly
		 * @retval	1 - if any error occured or setting was not found
		 **/
		static __forceinline INT GCIFirstLast( CONTACTINFO *ci )
		{
			DBVARIANT dbvf, dbvl;
			INT type, cbf, cbl;

			type = CI_TCHAR( ci );

			if( DB::Setting::GetEx( ci->hContact, USERINFO, ci->szProto, SET_CONTACT_FIRSTNAME, &dbvf, type ) )
			{
				dbvf.type = DBVT_DELETED;
			}
			if( DB::Setting::GetEx( ci->hContact, USERINFO, ci->szProto, SET_CONTACT_LASTNAME, &dbvl, type ) )
			{
				dbvl.type = DBVT_DELETED;
			}

			if( type == DBVT_WCHAR )
			{
				// both firstname and lastname are valid
				if( dbvf.type == DBVT_WCHAR && dbvl.type == DBVT_WCHAR )
				{
					cbf = mir_wcslen( dbvf.pwszVal );
					cbl = mir_wcslen( dbvl.pwszVal );

					ci->pszVal = ( LPTSTR ) mir_alloc( ( cbf + cbl + 2 ) * sizeof( WCHAR ) );
					if( ci->pszVal )
					{
						mir_snprintfW( ( LPWSTR ) ci->pszVal, cbf + cbl + 2, L"%s %s", dbvf.pwszVal, dbvl.pwszVal );
					}
					DB::Variant::Free( &dbvf );
					DB::Variant::Free( &dbvl );
				}
				// set firstname as result
				else if( dbvf.type == DBVT_WCHAR )
				{
					ci->pszVal = ( LPTSTR ) dbvf.pwszVal;
					DB::Variant::Free( &dbvl );
				}
				// set lastname as result
				else if( dbvl.type == DBVT_WCHAR )
				{
					ci->pszVal = ( LPTSTR ) dbvl.pwszVal;
					DB::Variant::Free( &dbvf );
				}
				else
				{
					ci->pszVal = NULL;
					DB::Variant::Free( &dbvf );
					DB::Variant::Free( &dbvl );
				}
			}
			else
			{
				// both firstname and lastname are valid
				if( dbvf.type == DBVT_ASCIIZ && dbvl.type == DBVT_ASCIIZ )
				{
					cbf = mir_strlen( dbvf.pszVal );
					cbl = mir_strlen( dbvl.pszVal );

					ci->pszVal = ( LPTSTR ) mir_alloc( ( cbf + cbl + 2 ) * sizeof( CHAR ) );
					if( ci->pszVal )
					{
						mir_snprintfA( ( LPSTR ) ci->pszVal, cbf + cbl + 2, "%s %s", dbvf.pszVal, dbvl.pszVal );
					}
					DB::Variant::Free( &dbvf );
					DB::Variant::Free( &dbvl );
				}
				// set firstname as result
				else if( dbvf.type == DBVT_ASCIIZ )
				{
					ci->pszVal = ( LPTSTR ) dbvf.pszVal;
					DB::Variant::Free( &dbvl );
				}
				// set lastname as result
				else if( dbvl.type == DBVT_ASCIIZ )
				{
					ci->pszVal = ( LPTSTR ) dbvl.pszVal;
					DB::Variant::Free( &dbvf );
				}
				else
				{
					ci->pszVal = NULL;
					DB::Variant::Free( &dbvf );
					DB::Variant::Free( &dbvl );
				}
			}
			ci->type = ( ci->pszVal != NULL ) ? CNFT_ASCIIZ : 0;
			return ci->type == 0;
		}

		/**
		 * return the country name
		 *
		 * @param		ci				- pointer to a CONTACTINFO structure holding information about the contact
		 * @param		szSetting	- the desired setting to read the countrys id from
		 *
		 * @retval	0 -	if setting was found and read correctly
		 * @retval	1 - if any error occured or setting was not found
		 **/
		static __forceinline INT GCICountry( CONTACTINFO *ci, LPCSTR szSetting )
		{
			if( GCIVarEx( ci, szSetting ) == 0 )
			{
				// country id was safed in database
				if( ci->type != CNFT_ASCIIZ )
				{
					LPSTR szCountry = ( LPSTR )CallService( MS_UTILS_GETCOUNTRYBYNUMBER, ci->wVal, 0 );
					if( szCountry )
					{
						ci->pszVal = ( ci->dwFlag & CNF_UNICODE ) 
											 ? ( LPTSTR ) mir_a2u( szCountry ) 
											 : ( LPTSTR ) mir_strdup( szCountry );
					}
					else
					{
						ci->pszVal = NULL;
					}
					ci->type = ( ci->pszVal != NULL ) ? CNFT_ASCIIZ : 0;
				}
			}
			return ci->type != CNFT_ASCIIZ;
		}

		/**
		 * This is the service procedure to retrieve contact information
		 *
		 * @param		wParam	 - not used
		 * @param		lParam	 - pointer to a CONTACTINFO structure which tells what information is desired
		 *
		 * @retval	0		- if contact information was found and read correctly 
		 * @retval	1		-	if any error occured or setting was not found
		 **/
		INT Get( WPARAM wParam, LPARAM lParam ) 
		{
			CONTACTINFO *ci = ( CONTACTINFO* ) lParam;
			INT result;

			if( ci && ci->cbSize == sizeof( CONTACTINFO ) && ( ci->szProto != NULL || ( ci->szProto = DB::Contact::Proto( ci->hContact ) ) != NULL ) )
			{
				switch( ci->dwFlag & 0x7F ) 
				{

				//
				// contact name
				//
				case CNF_FIRSTNAME:
					{
						result = GCIVarEx( ci, SET_CONTACT_FIRSTNAME );
					}
					break;
						
				case CNF_LASTNAME:
					{
						result = GCIVarEx( ci, SET_CONTACT_LASTNAME );
					}
					break;
				
				case CNF_NICK:
					{
						result = GCIVarEx( ci, SET_CONTACT_NICK );
					}
					break;
				
				case CNF_FIRSTLAST:
					{
						result = GCIFirstLast( ci );
					}
					break;

				case CNF_CUSTOMNICK:
					{
						LPSTR s = ci->szProto;

						ci->szProto = MOD_CLIST;
						result = GCIVar( ci, SET_CONTACT_MYHANDLE );
						ci->szProto = s;
					}
					break;
			
				//
				// private contact
				//
				case CNF_STREET:
					{
						result = GCIVarEx( ci, SET_CONTACT_STREET );
					}
					break;

				case CNF_ZIP:
					{
						result = GCIVarEx( ci, SET_CONTACT_ZIP ); 
					}
					break;

				case CNF_CITY:
					{
						result = GCIVarEx( ci, SET_CONTACT_CITY );
					}
					break;

				case CNF_STATE:
					{
						result = GCIVarEx( ci, SET_CONTACT_STATE );
					}
					break;

				case CNF_COUNTRY:
					{
						result = GCICountry( ci, SET_CONTACT_COUNTRY );
					}
					break;

				case CNF_PHONE:
					{
						result = GCIVarEx( ci, SET_CONTACT_PHONE );
					}
					break;

				case CNF_FAX:
					{
						result = GCIVarEx( ci, SET_CONTACT_FAX );
					}
					break;

				case CNF_CELLULAR:
					{
						result = GCIVarEx( ci, SET_CONTACT_CELLULAR );
					}
					break;

				case CNF_EMAIL:
					{
						result = GCIVarEx( ci, SET_CONTACT_EMAIL );
					}
					break;

				case CNF_HOMEPAGE:
					{
						result = GCIVarEx( ci, SET_CONTACT_HOMEPAGE );
					}
					break;

				//
				// company information
				//
				case CNF_CONAME:
					{
						result = GCIVarEx( ci, SET_CONTACT_COMPANY );
					}
					break;

				case CNF_CODEPT:
					{
						result = GCIVarEx( ci, SET_CONTACT_COMPANY_DEPARTMENT );
					}
					break;

				case CNF_COPOSITION:
					{
						result = GCIVarEx( ci, SET_CONTACT_COMPANY_POSITION );
					}
					break;

				case CNF_COSTREET:
					{
						result = GCIVarEx( ci, SET_CONTACT_COMPANY_STREET );
					}
					break;

				case CNF_COZIP:
					{
						result = GCIVarEx( ci, SET_CONTACT_COMPANY_ZIP );
					}
					break;

				case CNF_COCITY:
					{
						result = GCIVarEx( ci, SET_CONTACT_COMPANY_CITY );
					}
					break;

				case CNF_COSTATE:
					{
						result = GCIVarEx( ci, SET_CONTACT_COMPANY_STATE );
					}
					break;

				case CNF_COCOUNTRY:
					{
						result = GCICountry( ci, SET_CONTACT_COMPANY_COUNTRY );
					}
					break;

				case CNF_COPHONE:
					{
						result = GCIVarEx( ci, SET_CONTACT_COMPANY_PHONE );
					}
					break;

				case CNF_COFAX:
					{
						result = GCIVarEx( ci, SET_CONTACT_COMPANY_FAX );
					}
					break;

				case CNF_COCELLULAR:
					{
						result = GCIVarEx( ci, SET_CONTACT_COMPANY_CELLULAR );
					}
					break;

				case CNF_COEMAIL:
					{
						result = GCIVarEx( ci, SET_CONTACT_COMPANY_EMAIL );
					}
					break;

				case CNF_COHOMEPAGE:
					{
						result = GCIVarEx( ci, SET_CONTACT_COMPANY_HOMEPAGE );
					}
					break;

				//
				// personal information
				//
				case CNF_ABOUT:
					{
						result = GCIVarEx( ci, SET_CONTACT_ABOUT );
					}
					break;

				case CNF_MYNOTES:
					{
						result = GCIVarEx( ci, SET_CONTACT_MYNOTES );
					}
					break;

				case CNF_AGE:
					{
						result = GCIVarEx( ci, SET_CONTACT_AGE );
					}
					break;

				case CNF_GENDER:
					{
						ci->bVal = NServices::Gender::Get( ci->hContact, ci->szProto );
						ci->type = ( ci->bVal != 0 ) ? CNFT_BYTE : 0;
						result = ci->type == 0;
					}
					break;

				case CNF_BIRTHDAY:
					{
						MAnnivDate mda;

						result = mda.DBGetBirthDate( ci->hContact, ci->szProto );
						if( result == 0 )
						{
							ci->bVal = ( BYTE ) mda.Day();
							ci->type = CNFT_BYTE;
						}
					}
					break;

				case CNF_BIRTHMONTH:
					{
						MAnnivDate mda;

						result = mda.DBGetBirthDate( ci->hContact, ci->szProto );
						if( result == 0 )
						{
							ci->bVal = ( BYTE ) mda.Month();
							ci->type = CNFT_BYTE;
						}
					}
					break;

				case CNF_BIRTHYEAR:	
					{
						MAnnivDate mda;
						
						result = mda.DBGetBirthDate( ci->hContact, ci->szProto );
						if( result == 0 )
						{
							ci->wVal = ( WORD ) mda.Year();
							ci->type = CNFT_WORD;
						}
					}
					break;

				case CNF_BIRTHDATE:
					{
						MAnnivDate mda;

						result = mda.DBGetBirthDate( ci->hContact, ci->szProto );
						if( result == 0 )
						{					
							SYSTEMTIME st;

							st = mda.SystemTime();

							ci->pszVal = NULL;
							if( ci->dwFlag & CNF_UNICODE ) 
							{
								WCHAR wszDate[80];
								
								if( GetDateFormatW( LOCALE_USER_DEFAULT, wParam == 1 ? DATE_LONGDATE : DATE_SHORTDATE, &st, NULL, wszDate, SIZEOF( wszDate ) ) ) {
									ci->pszVal = ( LPTSTR )mir_wcsdup( wszDate );
								}
							}
							else 
							{
								CHAR szDate[80];
								
								if( GetDateFormatA( LOCALE_USER_DEFAULT, wParam == 1 ? DATE_LONGDATE : DATE_SHORTDATE, &st, NULL, szDate, SIZEOF( szDate ) ) ) {
									ci->pszVal = ( LPTSTR )mir_strdup( szDate );
								}
							}
							ci->type = ( ci->pszVal != NULL ) ? CNFT_ASCIIZ : 0;
							result = ci->type == 0;
						}
					}
					break;

				case CNF_TIMEZONE:
					{
						DWORD dwIndex;
						CTimeZone* ptz;
						
						ptz = NServices::TimeZones::Manager.GetContactTimeZone( ci->hContact, ci->szProto, dwIndex );
						if( ptz )
						{
							if( ci->dwFlag & CNF_UNICODE ) 
							{
								ci->pszVal = ( LPTSTR ) mir_t2u( ptz->pszDisplay );
							}
							else
							{
								ci->pszVal = ( LPTSTR ) mir_t2a( ptz->pszDisplay );
							}
						}
						else
						{
							CHAR c  = ( CHAR ) dwIndex;
							if( c >= -24 && c <= 24 )
							{
								if( ci->dwFlag & CNF_UNICODE ) 
								{
									WCHAR str[80];

									if( mir_snprintfW( str, SIZEOF( str ), c ? L"GMT%+d:%02d" : L"GMT", -c / 2, ( c & 1 ) * 30 ) > 0 )
									{
										ci->pszVal = ( LPTSTR ) mir_wcsdup( str );
									}
									else
									{
										ci->pszVal = NULL;
									}
								}
								else 
								{
									CHAR str[80];

									if( mir_snprintfA( str, SIZEOF( str ), c ? "GMT%+d:%02d" : "GMT", -c / 2, ( c & 1 ) * 30 ) > 0 )
									{
										ci->pszVal = ( LPTSTR ) mir_strdup( str );
									}
									else
									{
										ci->pszVal = NULL;
									}
								}
							}
							else
							{
								ci->pszVal = NULL;
							}
						}
						ci->type = ( ci->pszVal != NULL ) ? CNFT_ASCIIZ : 0;
						result = ci->type == 0;
					}
					break;

				//
				// information about IM specific stuff
				//
				case CNF_UNIQUEID:
					{
						// protocol must define a PFLAG_UNIQUEIDSETTING
						result = CallProtoService( ci->szProto, PS_GETCAPS, PFLAG_UNIQUEIDSETTING, 0 );
						if( result != CALLSERVICE_NOTFOUND && result != NULL ) 
						{	
							result = GCIVar( ci, ( LPCSTR ) result );
						}
					}
					break;

				case CNF_DISPLAYNC:
				case CNF_DISPLAY:
					{
						INT i;
						for( i = 0; i < NAMEORDERCOUNT; i++ ) 
						{
							switch( gNameOrder[i] ) 
							{
							case 0: // custom name
								{
									// make sure we aren't in CNF_DISPLAYNC mode
									// don't get custom name for NULL contact
									if( ci->hContact != NULL && ( ci->dwFlag & 0x7F ) == CNF_DISPLAY ) 
									{
										BYTE dwFlag = ci->dwFlag;
										ci->dwFlag = ( ci->dwFlag & CNF_UNICODE ) | CNF_CUSTOMNICK;
										if( !Get( NULL, ( LPARAM )ci ) ) 
										{
											ci->dwFlag = dwFlag;
											return 0;
										}
										ci->dwFlag = dwFlag;
									}
								}
								break;

							case 1: // nick
								{
									if( !GCIVarEx( ci, SET_CONTACT_NICK ) )
									{
										return 0;
									}
								}
								break;

							case 2: // First Name
								{
									if( !GCIVarEx( ci, SET_CONTACT_FIRSTNAME ) )
									{
										return 0;
									}
								}
								break;

							case 3: // E-mail
								{
									if( !GCIVarEx( ci, SET_CONTACT_EMAIL ) )
									{
										return 0;
									}
								}
								break;

							case 4: // Last Name
								{
									if( !GCIVarEx( ci, SET_CONTACT_LASTNAME ) )
									{
										return 0;
									}
								}
								break;

							case 5: // Unique id
								{
									// protocol must define a PFLAG_UNIQUEIDSETTING
									result = CallProtoService( ci->szProto, PS_GETCAPS, PFLAG_UNIQUEIDSETTING, 0 );
									if( result != CALLSERVICE_NOTFOUND && result != NULL ) 
									{	
										if( !GCIStr( ci, ( LPCSTR ) result ) )
										{
											return 0;
										}
									}
								}
								break;

							case 6: // first + last name
								{
									if( !GCIFirstLast( ci ) )
									{
										return 0;
									}
								}
								break;

							default: // unknown contact
								{
									if( ci->dwFlag & CNF_UNICODE )
									{
										ci->pszVal = ( LPTSTR ) mir_wcsdup( TranslateW( L"'( Unknown Contact )'" ) );
									}
									else
									{
										ci->pszVal = ( LPTSTR ) mir_strdup( Translate( "'( Unknown Contact )'" ) );
									}
									ci->type = ( ci->pszVal != NULL ) ? CNFT_ASCIIZ : 0;
									return ci->type == 0;
								}
							}
						}
					}

				default:
					{
						result = 1;
					}
				}
			}
			else
			{
				result = 1;
			}
			return result;
		}

		/**
		 * This is the implementation of the MS_DB_CONTACT_GETSETTING_STR_EX service.
		 *
		 * @param		wParam	- handle of the contact a setting was written for ( must be NULL in this case )
		 * @param		lParam	- DBCONTACTGETSETTING structure holding information about the setting to read
		 *
		 * @retval	0 - success
		 * @retval	1 - error
		 **/
		static INT GetContactSettingStrExService( WPARAM wParam, LPARAM lParam )
		{
			DBCONTACTGETSETTING *cgs = ( DBCONTACTGETSETTING* )lParam;
			return DB::Setting::GetEx( ( HANDLE )wParam, USERINFO, cgs->szModule, cgs->szSetting, cgs->pValue, cgs->pValue->type );
		}

		/**
		 * If the user changes the name order update the global value.
		 *
		 * @param		wParam	- handle of the contact a setting was written for ( must be NULL in this case )
		 * @param		lParam	- DBCONTACTWRITESETTING structure holding information about the written data
		 * @return	0
		 **/
		static INT OnSettingChanged( WPARAM wParam, LPARAM lParam )
		{
			if( wParam == NULL ) 
			{
				DBCONTACTWRITESETTING *cws = ( DBCONTACTWRITESETTING* ) lParam;
				
				if(
					!mir_strcmp( cws->szModule, "Contact" ) &&
					!mir_strcmp( cws->szSetting, "NameOrder" )
					 )
				{
					memcpy( gNameOrder, cws->value.pbVal, sizeof( gNameOrder ) );
				}
			}
			return 0;
		}

		/**
		 * Loads the module at startup and replaces the service.
		 *
		 * @param		none
		 * @return	nothing
		 **/
		VOID LoadModule()
		{
			CreateServiceFunction( MS_DB_CONTACT_GETSETTING_STR_EX, GetContactSettingStrExService );

			if( DB::Setting::GetByte( SET_GETCONTACTINFO_ENABLED, DEFVAL_GETCONTACTINFO_ENABLED ) ) 
			{
				if( DestroyServiceFunction( ( HANDLE ) hashSetting( MS_CONTACT_GETCONTACTINFO ) ) == 0 )
				{
					if( CreateServiceFunction( MS_CONTACT_GETCONTACTINFO, Get ) )
					{
						DBVARIANT dbv;
						
						if( DB::Setting::GetAString( NULL, "Contact", "NameOrder", &dbv ) ) 
						{
							BYTE i;

							for( i = 0; i < NAMEORDERCOUNT; i++ )
							{
								gNameOrder[i] = i;
							}
						}
						else
						{
							memcpy( gNameOrder, dbv.pbVal, sizeof( gNameOrder ) );
							DB::Variant::Free( &dbv );
						}
				
						HookEvent( ME_DB_CONTACT_SETTINGCHANGED, OnSettingChanged );
					}
				}
			}
		}

	} /* namespace ContactInfo */

} /* namespace NServices  */
